﻿using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json;
using Report;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;

namespace AutoUpdate.Work
{
    public class UpdateWork
    {
        internal delegate void InfoNotify(string str);
        internal delegate void DownloadProgress(int progress);
        internal event InfoNotify InfoEvent;
        internal event DownloadProgress DownloadEvent;

        private const string APP_NAME = "Gui";
        private const string CHECK_URL = "http://10.3.1.134:8080/api/check/version";
        private const string REG_PATH = "SOFTWARE\\IndustryPad\\";
        private const string VERSION_REG_KEY = "version";

        private readonly Register register = new Register(REG_PATH, RegDomain.CurrentUser);
        private AutoResetEvent autoResetEvent = new AutoResetEvent(false);

        internal class VersionInfo
        {
            public string version;
            public string downloadUrl;
            public string description;
        }

        public static bool HasNewVersion(out string version)
        {
            UpdateWork updateWork = new UpdateWork();
            VersionInfo latestVersion = updateWork.GetLastVersion();
            string localVersion = updateWork.LocalVersion();
            version = latestVersion.version;
            return localVersion != version;
        }

        internal void StartUpdate()
        {
            try
            {
                InfoEvent("开始更新");
                KillProcess();

                VersionInfo newVersion = GetLastVersion();
                InfoEvent("更新版本:" + newVersion.version);
                InfoEvent("更新内容:");
                InfoEvent(newVersion.description);

                InfoEvent("正在下载文件");
                string downloadZipPath = Download(newVersion.downloadUrl);

                autoResetEvent.WaitOne();

                InfoEvent("正在解压文件");
                string unZipDirPath = UnZip(downloadZipPath);

                InfoEvent("正在替换文件");
                ReplaceFiles(unZipDirPath, AppDomain.CurrentDomain.BaseDirectory);

                InfoEvent("正在清理临时文件");
                register.WriteRegeditKey(VERSION_REG_KEY, newVersion.version);
                File.Delete(downloadZipPath);
                Directory.Delete(unZipDirPath, true);

                InfoEvent("程序升级完毕");
            }
            catch (Exception e)
            {
                InfoEvent("更新异常:" + e.Message);
                ReportUtil.ReportError(e);
            }
        }

        internal string LocalVersion()
        {
            object ret = register.ReadRegeditKey(VERSION_REG_KEY);
            if (ret == null)
            {
                Console.WriteLine("未获取到本地版本号");
                return "未知";
            }
            return ret.ToString();
        }

        internal VersionInfo GetLastVersion()
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(CHECK_URL);
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8);
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();
            VersionInfo versionInfo= JsonConvert.DeserializeObject<VersionInfo>(retString);
            return versionInfo;
        }
        private string Download(string downloadUrl)
        {
            WebClient client = new WebClient();
            string fileNewName = Guid.NewGuid() + ".zip";
            string downloadFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileNewName);

            client.DownloadFileCompleted += Client_DownloadFileCompleted;
            client.DownloadProgressChanged += Client_DownloadProgressChanged;
            client.DownloadFileAsync(new Uri(downloadUrl), downloadFilePath);

            return downloadFilePath;
        }

        /// <summary>
        /// ZIP:解压一个zip文件
        /// </summary>
        /// <param name="ZipFilePath">需要解压的Zip文件（绝对路径）</param>
        /// <return>解压到的目录</return>
        private string UnZip(string ZipFilePath)
        {
            string zipFilename = Path.GetFileName(ZipFilePath);
            string targetDirName = zipFilename.Substring(0, zipFilename.LastIndexOf('.') + 1);
            string targetDirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, targetDirName);

            using (ZipInputStream zipfiles = new ZipInputStream(File.OpenRead(ZipFilePath)))
            {
                ZipEntry theEntry;
                while ((theEntry = zipfiles.GetNextEntry()) != null)
                {
                    string pathToZip = theEntry.Name;
                    string directoryName = Path.GetDirectoryName(pathToZip);

                    string fileName = Path.GetFileName(pathToZip);
                    string realDirPath = Path.Combine(targetDirPath, directoryName);
                    Directory.CreateDirectory(realDirPath);

                    if (fileName != "")
                    {
                        string filepath = Path.Combine(realDirPath, fileName);
                        using (FileStream streamWriter = File.Create(filepath))
                        {
                            byte[] data = new byte[1024];
                            int readCount;
                            while ((readCount = zipfiles.Read(data, 0, data.Length)) > 0)
                            {
                                streamWriter.Write(data, 0, readCount);
                            }
                            streamWriter.Close();
                        }
                    }
                }
                zipfiles.Close();
            }
            return targetDirPath;
        }

        private void KillProcess()
        {
            Process[] processes = Process.GetProcesses();
            foreach (Process pr in processes)
            {
                if (APP_NAME == pr.ProcessName)
                {
                    pr.Kill();
                }
            }
        }

        private void ReplaceFiles(string newFileDir, string oldFileDir)
        {
            var newFileInfos = new DirectoryInfo(newFileDir).GetFileSystemInfos();
            IEnumerable<string> newFileNames = newFileInfos.Select(newFile => newFile.Name);

            var oldFileInfos = new DirectoryInfo(oldFileDir).GetFileSystemInfos();
            var willRemoveFiles = oldFileInfos.Where(oldFile =>
                 newFileNames.Contains(oldFile.Name)
            );
            foreach (FileSystemInfo fsi in willRemoveFiles)
            {
                if (fsi.Attributes == FileAttributes.Directory)
                {
                    var dirInfo = (DirectoryInfo)fsi;
                    dirInfo.Delete(true);
                }
                else
                    fsi.Delete();
            }

            foreach (FileSystemInfo fsi in newFileInfos)
            {
                if (fsi.Attributes == FileAttributes.Directory)
                {
                    var dir = (DirectoryInfo)fsi;
                    string destPath = Path.Combine(oldFileDir, dir.Name);
                    dir.MoveTo(destPath);
                }
                else
                {
                    var file = (FileInfo)fsi;
                    string destPath = Path.Combine(oldFileDir, file.Name);
                    file.MoveTo(destPath);
                }
            }
        }

        private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            DownloadEvent(e.ProgressPercentage);
        }

        private void Client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            autoResetEvent.Set();
        }
    }
}
